package com.example.inventory.controller;

import com.example.inventory.model.ProductInventory;
import com.example.inventory.request.ProductInventoryCacheRefreshRequest;
import com.example.inventory.request.ProductInventoryDBUpdateRequest;
import com.example.inventory.request.Request;
import com.example.inventory.service.AsyncRequestProcessService;
import com.example.inventory.service.ProductInventoryService;
import com.example.inventory.vo.Response;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import java.util.Objects;

/**
 *
 */
@Controller
public class ProductInventoryController {

    @Resource
    private ProductInventoryService productInventoryService;

    @Resource
    private AsyncRequestProcessService asyncRequestProcessService;

    /**
     * 更新商品库存
     */
    @GetMapping("/updateProductInventory")
    @ResponseBody
    public Object updateProductInventory(ProductInventory productInventory) {

        System.out.println("===========日志===========: 接收到更新商品库存的请求，商品id=" + productInventory.getProductId() + ", 商品库存数量=" + productInventory.getInventoryCnt());
        Response response;
        try {
            String taskName = String.format("pid:[%d]库存更新请求[先去删除redis缓存,然后修改数据库]", productInventory.getProductId());
            Request updateDBRequest = new ProductInventoryDBUpdateRequest(
                    taskName,
                    productInventory, productInventoryService);
            asyncRequestProcessService.process(updateDBRequest);
            response = new Response(Response.SUCCESS);
        } catch (Exception e) {
            e.printStackTrace();
            response = new Response(Response.FAILURE);
        }
        return response;
    }

    /**
     * 获取商品库存
     */
    @GetMapping("/getProductInventory")
    @ResponseBody
    public ProductInventory getProductInventory(Integer productId) throws InterruptedException {

        // System.out.println("===========日志===========: 接收到一个商品库存的读请求，商品id=" + productId);

        ProductInventory result = null;

        Request request = new ProductInventoryCacheRefreshRequest("读请求",
                productId, productInventoryService, false);

        asyncRequestProcessService.process(request);

        // 将请求扔到service异步处理以后, 就需要等待一会,
        // 去尝试等待前面有商品库存更新的操作, 同时缓存刷新的操作,将最新的数据刷新到缓存中
        // 我这里死循环去读redis就ok啦

        long startTime = System.currentTimeMillis();
        long endTime = 0L;
        long waitTime = 0L;

        // 等200ms没有从redis中获取到结果, 就去数据库拿呗
        while (true) {
            if (waitTime > 500) {
                break;
            }
            // 尝试去redis中读取一次商品库存的缓存数据
            result = productInventoryService.getProductInventoryCache(productId);

            // 如果读取到结果就返回
            if (Objects.nonNull(result)) {
                System.out.println("===========日志===========: 在200ms内读取到了redis中的库存缓存，商品id=" + result.getProductId() + ", 商品库存数量=" + result.getInventoryCnt());
                return result;
            } else {
                // 如果没有读到结果,那么等待一会儿时间
                Thread.sleep(20);
                endTime = System.currentTimeMillis();
                waitTime = endTime - startTime;
            }
        }

        // 如果跳转到这里 代表一样东西: 这个问题可能触发
        result = productInventoryService.findProductInventory(productId);
        if (Objects.nonNull(result)) {
            // 将缓存刷新一下
            // 这个过程, 实际上是一个读操作的过程, 但是没有在队列里串行出处理,
            // 还是有数据不一致性的问题
            // System.err.println("================强制刷新缓存把======================");
            Request forcedRefreshRequest = new ProductInventoryCacheRefreshRequest("强制刷新缓存", productId, productInventoryService, true);
            asyncRequestProcessService.process(forcedRefreshRequest);

            // 代码会运行到这里，只有三种情况：
            // 1、就是说，上一次也是读请求，数据刷入了redis，但是redis LRU算法给清理掉了，标志位还是false
            // 所以此时下一个读请求是从缓存中拿不到数据的，再放一个读Request进队列，让数据去刷新一下
            // 2、可能在200ms内，就是读请求在队列中一直积压着，没有等待到它执行（在实际生产环境中，基本是比较坑了）
            // 所以就直接查一次库，然后给队列里塞进去一个刷新缓存的请求
            // TODO 3、数据库里本身就没有，缓存穿透，穿透redis，请求到达mysql库

            return result;
        }

        return new ProductInventory(productId, -1L);
    }
}
